﻿namespace WebServiceExtensions.Common
{
	using System;
	using System.IO;
	using System.Runtime.Serialization;
	using System.Runtime.Serialization.Formatters.Binary;
	using System.Runtime.Serialization.Formatters.Soap;
	using System.Xml.Serialization;

    /// <summary>
    /// Type of the serializer
    /// </summary>
	public enum SerializerType
	{
        /// <summary>
        /// XML Serializer
        /// </summary>
		XmlSerializer,

        /// <summary>
        /// Binary Formatter
        /// </summary>
		BinaryFormatter,

        /// <summary>
        /// Soap Formatter
        /// </summary>
		SoapFormatter,
	}

    /// <summary>
    /// Serializer for serialization
    /// </summary>
    /// <typeparam name="T">a class</typeparam>
	public class Serializer<T> where T : class
	{
        /// <summary>
        /// Initializes a new instance of the <see cref="Serializer&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="type">The type.</param>
        public Serializer(SerializerType type)
        {
            this.SerializerType = type;
            switch (SerializerType)
            {
                case SerializerType.XmlSerializer:
                    {
                        Type systemType = typeof(T);
                        XmlSerializer = new XmlSerializer(systemType);
                        break;
                    }

                case SerializerType.SoapFormatter:
                    {
                        this.Formatter = new SoapFormatter();
                        break;
                    }

                case SerializerType.BinaryFormatter:
                    {
                        this.Formatter = new BinaryFormatter();
                        break;
                    }
            }
        }

        /// <summary>
        /// Gets or sets the type of the serializer.
        /// </summary>
        /// <value>The type of the serializer.</value>
		public SerializerType SerializerType { get; set; }

        /// <summary>
        /// Gets the XML serializer.
        /// </summary>
		public XmlSerializer XmlSerializer { get; private set; }

        /// <summary>
        /// Gets the formatter.
        /// </summary>
		public IFormatter Formatter { get; private set; }

        /// <summary>
        /// Serializes the specified stream.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <param name="o">The object.</param>
        public void Serialize(Stream stream, T o)
        {
            switch (SerializerType)
            {
                case SerializerType.XmlSerializer:
                    {
                        XmlSerializer.Serialize(stream, o);
                        break;
                    }

                case SerializerType.SoapFormatter:
                case SerializerType.BinaryFormatter:
                    {
                        this.Formatter.Serialize(stream, o);
                        break;
                    }
            }
        }

        /// <summary>
        /// Serializes the specified object.
        /// </summary>
        /// <param name="o">The object.</param>
        /// <returns>object serialized to string</returns>
        public string Serialize(T o)
        {
            MemoryStream stream = new MemoryStream();
            switch (SerializerType)
            {
                case SerializerType.XmlSerializer:
                    {
                        XmlSerializer.Serialize(stream, o);
                        break;
                    }

                case SerializerType.SoapFormatter:
                case SerializerType.BinaryFormatter:
                    {
                        this.Formatter.Serialize(stream, o);
                        break;
                    }
            }

            if (stream.CanSeek)
            {
                stream.Seek(0, SeekOrigin.Begin);
            }

            StreamReader streamReader = new StreamReader(stream);
            return streamReader.ReadToEnd();
        }

		/// <summary>
		/// Deserializes the specified stream.
		/// </summary>
		/// <param name="stream">The stream.</param>
		/// <returns>the object</returns>
		public T Deserialize(Stream stream)
		{
			T obj = null;
			switch (SerializerType)
			{
				case SerializerType.XmlSerializer:
					{
						obj = (T)XmlSerializer.Deserialize(stream);
						break;
					}

				case SerializerType.SoapFormatter:
				case SerializerType.BinaryFormatter:
					{
						obj = (T)this.Formatter.Deserialize(stream);
						break;
					}
			}

			return obj;
		}
	}
}